﻿using Prism.Navigation;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using Telerik.Windows.Controls;

namespace SmartPlant.Core.Mvvm
{
    /// <summary>
    /// 视图模型基类。
    /// </summary>
    public abstract class SmartViewModel : ViewModelBase, IDestructible
    {
        /// <summary>
        /// 设置指定的属性值，并触发属性值更改事件。
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="storage"></param>
        /// <param name="value"></param>
        /// <param name="propertyName"></param>
        /// <returns></returns>
        protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
        {
            if (EqualityComparer<T>.Default.Equals(storage, value))
            {
                return false;
            }
            storage = value;
            RaisePropertyChanged(propertyName);
            return true;
        }

        public virtual void Destroy()
        {

        }
    }


    /// <summary>
    /// 提供数据实体绑定进行验证的数据模型。
    /// </summary>
    public abstract class BindableValidatorViewModel : SmartViewModel
    {
        private readonly INotifyPropertyChanged _entityToValidate;
        private IDictionary<string, ReadOnlyCollection<string>> _errors = new Dictionary<string, ReadOnlyCollection<string>>();
        /// <summary>
        /// 错误更改通知的多播事件。
        /// </summary>
        public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;

        /// <summary>
        /// 如果启用了验证功能，则返回true。否则为false。
        /// </summary>
        public bool IsValidationEnabled { get; set; }

        /// <summary>
        /// 表示一个空错误值的集合。
        /// </summary>
        public static readonly ReadOnlyCollection<string> EmptyErrorsCollection = new ReadOnlyCollection<string>(new List<string>());

        private Func<string, string, string> _getResourceDelegate;

        /// <summary>
        /// 使用要验证的实体初始化<see cref="BindableValidatorViewModel"/>类的新实例。
        /// </summary>
        /// <param name="entityToValidate">要验证的实体</param>
        /// <param name="getResourceDelegate">一个委托，返回给定资源映射Id和资源Id的字符串资源.</param>
        /// <exception cref="ArgumentNullException">当<paramref name="entityToValidate"/> 为空 <see langword="null" /> 时抛出异常.</exception>
        public BindableValidatorViewModel(INotifyPropertyChanged entityToValidate, Func<string, string, string> getResourceDelegate)
            : this(entityToValidate)
        {
            _getResourceDelegate = getResourceDelegate;
        }

        /// <summary>
        /// 使用要验证的实体初始化<see cref="BindableValidatorViewModel"/>类的新实例。
        /// </summary>
        /// <param name="entityToValidate">要验证的实体</param>
        /// <exception cref="ArgumentNullException">当<paramref name="entityToValidate"/> 为空 <see langword="null" /> 时抛出异常.</exception>
        public BindableValidatorViewModel(INotifyPropertyChanged entityToValidate)
        {
            if (entityToValidate == null)
            {
                throw new ArgumentNullException(nameof(entityToValidate));
            }

            _entityToValidate = entityToValidate;
            IsValidationEnabled = true;
            _getResourceDelegate = (mapId, key) =>
            {
                //var resourceLoader = ResourceLoader.GetForCurrentView(mapId);
                //return resourceLoader.GetString(key);
                return "";
            };
        }



        /// <summary>
        /// 返回属性的错误。
        /// </summary>
        /// <param name="propertyName">属性的名称。</param>
        /// <returns>属性的错误（如果有错误）。否则，BindableValidatorViewModel.EmptyErrorsCollection。</returns>
        public ReadOnlyCollection<string> this[string propertyName]
        {
            get
            {
                return _errors.ContainsKey(propertyName) ? _errors[propertyName] : EmptyErrorsCollection;
            }
        }

        /// <summary>
        /// 获取每个属性的错误列表。
        /// </summary>
        /// <value>
        /// 属性名称和错误集合对的字典。
        /// </value>
        public IDictionary<string, ReadOnlyCollection<string>> Errors
        {
            get { return _errors; }
        }

        /// <summary>
        /// 返回一个新的ReadOnlyDictionary，该字典包含实体的所有错误，并由属性分隔。
        /// </summary>
        public ReadOnlyDictionary<string, ReadOnlyCollection<string>> GetAllErrors()
        {
            return new ReadOnlyDictionary<string, ReadOnlyCollection<string>>(_errors);
        }

        /// <summary>
        /// 更新实体的错误集合，通知错误集合是否已更改。
        /// </summary>
        /// <param name="entityErrors">实体的错误集合。</param>
        public void SetAllErrors(IDictionary<string, ReadOnlyCollection<string>> entityErrors)
        {
            if (entityErrors == null)
            {
                throw new ArgumentNullException(nameof(entityErrors));
            }

            _errors.Clear();

            foreach (var item in entityErrors)
            {
                SetPropertyErrors(item.Key, item.Value);
            }

            OnPropertyChanged("Item[]");
            OnErrorsChanged(string.Empty);
        }

        /// <summary>
        /// 根据属性ValidationAttributes属性中设置的规则验证属性。
        /// 它使用新的验证结果更新错误集合（如有必要，会发出通知）。
        /// </summary>
        /// <param name="propertyName">要验证的属性的名称。</param>
        /// <returns>如果属性有效，则为True。否则为false。</returns>
        /// <exception cref="ArgumentNullException">当 <paramref name="propertyName"/> 为 <see langword="null" /> 或空字符串抛出异常.</exception>
        /// <exception cref="ArgumentException">当 <paramref name="propertyName"/> 参数与任何属性名称都不匹配抛出异常。</exception>
        public bool ValidateProperty(string propertyName)
        {
            if (string.IsNullOrEmpty(propertyName))
            {
                throw new ArgumentNullException(nameof(propertyName));
            }

            var propertyInfo = _entityToValidate.GetType().GetRuntimeProperty(propertyName);
            if (propertyInfo == null)
            {
                //var errorString = _getResourceDelegate(Constants.InfrastructureResourceMapId, "InvalidPropertyNameException");
                var errorString = "";
                throw new ArgumentException(errorString, propertyName);
            }

            var propertyErrors = new List<string>();
            bool isValid = TryValidateProperty(propertyInfo, propertyErrors);
            bool errorsChanged = SetPropertyErrors(propertyInfo.Name, propertyErrors);

            if (errorsChanged)
            {
                OnErrorsChanged(propertyName);
                OnPropertyChanged(string.Format(CultureInfo.CurrentCulture, "Item[{0}]", propertyName));
            }

            return isValid;
        }

        /// <summary>
        /// 验证用ValidationAttribute属性修饰的所有属性。
        /// 它使用新的验证结果更新每个属性错误集合（如有必要，会发出通知）。
        /// </summary>
        /// <returns>如果属性有效，则为True。否则为false。</returns>
        public bool ValidateProperties()
        {
            var propertiesWithChangedErrors = new List<string>();

            var propertiesToValidate = _entityToValidate.GetType()
                                                        .GetRuntimeProperties()
                                                        .Where(c => c.GetCustomAttributes(typeof(ValidationAttribute)).Any());

            foreach (PropertyInfo propertyInfo in propertiesToValidate)
            {
                var propertyErrors = new List<string>();
                TryValidateProperty(propertyInfo, propertyErrors);

                bool errorsChanged = SetPropertyErrors(propertyInfo.Name, propertyErrors);
                if (errorsChanged && !propertiesWithChangedErrors.Contains(propertyInfo.Name))
                {
                    propertiesWithChangedErrors.Add(propertyInfo.Name);
                }
            }

            foreach (string propertyName in propertiesWithChangedErrors)
            {
                OnErrorsChanged(propertyName);
                OnPropertyChanged(string.Format(CultureInfo.CurrentCulture, "Item[{0}]", propertyName));
            }

            return _errors.Values.Count == 0;
        }

        /// <summary>
        /// 对属性执行验证，将结果添加到propertyErrors列表中。
        /// </summary>
        /// <param name="propertyInfo">要验证的属性的<see cref="PropertyInfo"/></param>
        /// <param name="propertyErrors">包含属性的当前错误消息的列表。</param>
        /// <returns>如果属性有效，则为True。否则为false。</returns>
        private bool TryValidateProperty(PropertyInfo propertyInfo, List<string> propertyErrors)
        {
            var results = new List<ValidationResult>();
            var context = new ValidationContext(_entityToValidate) { MemberName = propertyInfo.Name };
            var propertyValue = propertyInfo.GetValue(_entityToValidate);

            bool isValid = Validator.TryValidateProperty(propertyValue, context, results);

            if (results.Any())
            {
                propertyErrors.AddRange(results.Select(c => c.ErrorMessage));
            }

            return isValid;
        }

        /// <summary>
        /// 更新属性的errors集合。
        /// </summary>
        /// <param name="propertyName">属性的名称。</param>
        /// <param name="propertyNewErrors">属性错误的新集合。</param>
        /// <returns>如果属性错误已更改，则为True。否则为false。</returns>
        private bool SetPropertyErrors(string propertyName, IList<string> propertyNewErrors)
        {
            bool errorsChanged = false;

            if (!_errors.ContainsKey(propertyName))
            {
                if (propertyNewErrors.Count > 0)
                {
                    _errors.Add(propertyName, new ReadOnlyCollection<string>(propertyNewErrors));
                    errorsChanged = true;
                }
            }
            else
            {
                if (propertyNewErrors.Count != _errors[propertyName].Count || _errors[propertyName].Intersect(propertyNewErrors).Count() != propertyNewErrors.Count)
                {
                    if (propertyNewErrors.Count > 0)
                    {
                        _errors[propertyName] = new ReadOnlyCollection<string>(propertyNewErrors);
                    }
                    else
                    {
                        _errors.Remove(propertyName);
                    }

                    errorsChanged = true;
                }
            }

            return errorsChanged;
        }

        /// <summary>
        /// 通知侦听器属性的错误已更改。
        /// </summary>
        /// <param name="propertyName">用于通知侦听器的属性的名称。</param>
        private void OnErrorsChanged(string propertyName)
        {
            ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName));
        }
    }

}
